今日文章目錄
- 前言
- useMemo()
- 實作紀錄
- 參考資料
- 後續
今天要練習 useMemo()
,不過爬了很多文,覺得還無法好好消化,這篇主要紀錄一些我找到的資料筆記,還有練習的過程。開始吧!
昨天我們在切換頁面的時候,透過Provider
value props把{state, setState}
傳下去,讓子層可以改值。
// Provider 傳值與方法:
function App() {
const [themsState, setThemeState] = useState(theme.light);
return (
<ThemeContext.Provider value={{ themsState, setThemeState }}>
...
</ThemeContext.Provider>
);
}
export default App;
function CategoryList(props) {
const { themsState, setThemeState } = useContext(ThemeContext);
return (...)
}
console.log()
看一下,會發現:Provider
傳什麼,useContext()
就會回傳什麼(就是最新值)。
官方文件有提到以下情況:
我想確定目前寫法:<Context.Provider value ={{state, setState}}></Context.Provider>
會不會造成上述的問題。搜了一輪資料,決定用useMemo inside Context API - React的方法來測試看看。
為了做個測試,我分別在父層ToDoPage
,子層CategoryList
TodoList
component內加入 console.log()
,來確認一下使用useMemo()
與沒有使用useMemo()
的差異。
ToDoPage
點擊按鈕更新值時,Provider props value 是否會重新建立,導致子層 CategoryList
ToDoList
re-render?CategoryList
父層加入 ThemeContext.Provider
value props :useMemo()
,當 dependency改變時,才更新回傳值。ToDoList
父層加入 ThemeContext.Provider
value props :{ themsState, setThemeState }
export default function ToDoPage() {
const [forceRender, setForceRender] = useState(false);
const [themsState, setThemeState] = useState(theme.light);
const themeContextProvider = useMemo(() => (
{ themsState, setThemeState }), [themsState, setThemeState]
);
return (
<>
{console.log('hey! I am in ToDoPage.')}
<MainLayout
menu={(
<ThemeContext.Provider value={themeContextProvider}>
<CategoryList />
</ThemeContext.Provider>
)}
content={(
<ThemeContext.Provider value={{ themsState, setThemeState }}>
<ToDoList />
</ThemeContext.Provider>
)}
/>
<Button onClick={() => setForceRender(!forceRender)}>Rerender Parent</Button>
</>
);
}
顯示效果:
ToDoList
或 CategoryList
切換 theme
,更新ThemeContext.Provider
值。
ToDoPage
點擊按鈕: 觸發 ToDoPage
CategoryList
ToDoList
re-render。
疑?看不出來 useMemo()
的效果
export default memo(CategoryList);
export default memo(ToDoList);
顯示效果:
ToDoList
或 CategoryList
切換 theme
,更新ThemeContext.Provider
值。
ToDoPage
點擊按鈕: 觸發 ToDoPage
ToDoList
re-render。CategoryList
沒有。
呼~終於有點收穫...
目前看起來的狀況:
測試A:父層狀態改變,子層re-render。
測試B:父層狀態改變,透過memo()
比對CategoryList
ToDoList
,根本沒有props
,所以不受父層狀態改變影響,唯一帶來改變的是ToDoList
的ThemeContext.Provider value={{ themsState, setThemeState }}
,每次都重新建立。
不過我應該把 console.log() 放一個在 useMemo()
比較準才對..
useMemo 小百科:
useMemo(() => computeExpensiveValue(a, b), [a, b]);
- 為了解決當元件本身改變狀態 re-render,避免元件內無關的邏輯重複渲染的效能問題。
- 用途:當dependency改變,才會執行
computeExpensiveValue(a, b)
重新計算,回傳一個 memoized 的值。- 參數:
computeExpensiveValue(a, b)
: 一function,回傳值會讓React記著,在dependency沒改變的情況下,會一直沿用該回傳值。[a, b]
: dependency。
memo 小百科:
memo(MyComponent, areEqual)
:
React 渲染機制,只要父層state
改變,不論子層接收的props
值是否有改變,子層一律強迫 re-render。memo()
是為了解決子層接收的props
導致重複渲染的效能問題。
- 用途:比對 更新前後
props
差異,沒有改變,會跳過這次 re-render。- 參數:
>MyComponent
: 作用元件
>areEqual *(optional)*
: 客制function。memo()
比對方式shallowly compare complex objects in the props object
,如果要做更深層的比對,可以自己寫。- If your function component wrapped in React.memo has a useState, useReducer or useContext Hook in its implementation, it will still rerender when state or context change. - 節錄React 官網。
Object.is(value1, value2)
React uses the
Object.is comparison algorithm
.
哇~真的把30天撐完了,好感動喔
10天React練習讓我有機會重新再回來看React 文件,我記得我剛開始接觸的時候,真的是有看沒有懂...目前寫了半年多,回來看文件會發現以前看不懂的,現在漸漸能理解它的意思,還有很多需要再練習,但覺得滿足。感謝鐵人賽!
這10天只有寫到一小部分,回頭看自己的文章,有些知識漏洞。之後會繼續補齊 use系列,不會一天一po,等我練到有點心得再繼續跟大家分享,恭喜各位鐵人完賽,下次見囉!